package morenoapp.model.impl;

import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipException;
import morenoapp.model.enums.MorenoPollType;
import morenoapp.model.data.MorenoModelBatchJob;
import morenoapp.model.impl.listeners.MorenoModelFire;
import morenoapp.model.enums.MorenoModelStage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import morenoapp.model.MorenoModelException;
import morenoapp.model.enums.MorenoQuestionType;
import morenoapp.model.enums.MorenoRespondentsSortOrder;
import morenoapp.model.enums.MorenoSex;
import morenoapp.model.data.MorenoAnswerFace;
import morenoapp.model.data.MorenoAnswerItemFace;
import morenoapp.model.data.MorenoModelData;
import morenoapp.model.data.MorenoQuestionFace;
import morenoapp.model.data.MorenoQuestionItemFace;
import morenoapp.model.data.MorenoRespondentFace;
import morenoapp.model.view2.MorenoView2Data;
import morenoapp.util.I18N;
import morenoapp.util.MorenoUtil;
import org.apache.commons.io.IOUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
 *
 * @author dmitry.mamonov
 */
public class MorenoModelImpl implements MorenoModelData, MorenoModelFire {
    private static final long serialVersionUID = 1L;
    protected static final String JSON_RESOURCE_QUESTIONS = "questions.json"; //NOI18N
    protected static final String JSON_RESOURCE_RESEARCH = "research.json"; //NOI18N
    protected static final String JSON_RESOURCE_RESPONDENTS = "respondents.json"; //NOI18N
    protected static final String JSON_RESOURCE_METADATA = "metadata.json"; //NOI18N
    protected static final String JSON_RESOURCE_ANSWERS = "answers.json"; //NOI18N
    protected static final String JSON_RESOURCE_VIEWS = "views.json"; //NOI18N
    /**
     * Тип опроса по умолчанию.
     */
    private static final MorenoPollType DEFAULT_POLL_TYPE = MorenoPollType.ParametricPoll;
    /**
     * Название модели.
     */
    protected String description;
    /**
     * Информация о исследователе.
     */
    protected String supervisorInfo = System.getProperties().getProperty("user.name");
    protected String inquirerIntro;
    protected String researchGroup;
    protected String researchName;
    protected String researchTarget;
    /**
     * Заключение исследователя по итогам анализа опроса.
     */
    private String conclusion;
    /**
     * Тип опроса.
     */
    private MorenoPollType pollType = DEFAULT_POLL_TYPE;
    /**
     * Максимальное количестно ответов на вопрос для исследования с
     * ограничением по числу ответов.
     */
    private int pollLimit = 0;
    /**
     * Дата проведения опроса.
     */
    private String pollDate = "";
    // *** metadata ***
    /**
     * Дата содаения модели.
     */
    protected Date creationDate = new Date();
    /**
     * Дата последнено сохранения модели.
     */
    protected Date lastSaveDate;
    /**
     * Место последнего сохранения модели.
     */
    protected URI storeURI;

    /*
     * Номер последнего изменения модели.
     */
    protected long version = 0;
    /**
     * Минимальная версия приложения с которой совместима данная модель исследования.
     */
    protected long versionCompatibility = 0; //TODO привести в божеский вид
    /**
     * База для генерации уникальных идентификаторов.
     */
    protected long lastId;
    // *** related data ***
    /**
     * Список респондентов.
     */
    protected List<MorenoRespondentFace> respondentsList = new ArrayList<MorenoRespondentFace>();
    /**
     * Вид на список респондентов, только для чтения.
     */
    protected transient List<MorenoRespondentFace> respondentsConstView;
    /**
     * Список вопросов.
     */
    protected List<MorenoQuestionFace> questionsList = new ArrayList<MorenoQuestionFace>();

    /*
     * Вид на список вопросов, только для чтения.
     */
    protected transient List<MorenoQuestionFace> questionsConstView;

    //protected MorenoSociogramViewImpl morenoSociogramView = new MorenoSociogramViewImpl();

    List<MorenoView2Data> viewsList = new ArrayList<MorenoView2Data>(){{
        add(new MorenoView2Impl());
    }};
    /**
     * Слушатель которому будут пренаправлятся все сообщения об изменениях отдельных
     * объектов модели.
     */
    protected transient MorenoModelFire fireDelegate = null;
    public MorenoModelImpl(MorenoModelFire _fireDelegate) {
        this.fireDelegate = _fireDelegate;
        initViews();
    }

    protected void initViews() {
        respondentsConstView = Collections.unmodifiableList(respondentsList);
        questionsConstView = Collections.unmodifiableList(questionsList);
    }

    @Override
    public String getDescription() {
        return notNull(description);
    }

    @Override
    public void setDescription(String description) {
        this.description = description;
    }

    @Override
    public Date getCreationDate() {
        return creationDate;
    }

    @Override
    public Date getLastSaveDate() {
        return lastSaveDate;
    }

    @Override
    public URI getStoreURI() {
        return storeURI;
    }

    @Override
    public long getVersion() {
        return version;
    }

    @Override
    public int getPollLimit() {
        return pollLimit;
    }

    @Override
    public String getConclusion() {
        return notNull(conclusion);
    }

    @Override
    public String getSupervisorInfo() {
        return notNull(supervisorInfo);
    }

    @Override
    public String getPollDate() {
        return pollDate;
    }

    @Override
    public MorenoPollType getPollType() {
        return pollType != null ? pollType : DEFAULT_POLL_TYPE;
    }

    @Override
    public long getVersionComaptibility() {
        return versionCompatibility;
    }

    @Override
    public void setPollLimit(int posistionCount) {
        this.pollLimit = posistionCount;
    }

    @Override
    public void setConclusion(String conclusion) {
        this.conclusion = conclusion;
    }

    @Override
    public void setSupervisorInfo(String supervisorInfo) {
        this.supervisorInfo = supervisorInfo;
    }

    @Override
    public void setPollDate(String pollDate) {
        this.pollDate = pollDate;
    }

    @Override
    public void setPollType(MorenoPollType pollType) {
        this.pollType = pollType;
    }

    @Override
    public long getLastId() {
        return lastId;
    }

    @Override
    public long nextId() {
        lastId++;
        return lastId;
    }

    @Override
    public void setLastId(long id) {
        this.lastId = id;
    }

    @Override
    public MorenoRespondentFace createRespondent() {
        MorenoRespondentImpl newRespondent = new MorenoRespondentImpl(this);
        respondentsList.add(newRespondent);
        return newRespondent;
    }

    @Override
    public List<MorenoRespondentFace> getRespondentsList() {
        return respondentsConstView;
    }

    @Override
    public MorenoQuestionFace createQuestion() {
        MorenoQuestionImpl newQuestion = new MorenoQuestionImpl(this);
        questionsList.add(newQuestion);
        return newQuestion;
    }

    @Override
    public boolean moveQuestionUp(MorenoQuestionFace _question) {
        int index = questionsList.indexOf(_question);
        if (index > 0) { //note: no need to move first element up
            MorenoQuestionFace prevQuestion = questionsList.get(index - 1);
            MorenoQuestionFace currQuestion = questionsList.get(index);
            questionsList.set(index - 1, currQuestion);
            questionsList.set(index, prevQuestion);
            return true;
        }
        return false;
    }

    @Override
    public boolean moveQuestionDown(MorenoQuestionFace _question) {
        int index = questionsList.indexOf(_question);
        if (index >= 0 && index + 1 < questionsList.size()) { //note: no need to move first element up
            MorenoQuestionFace currQuestion = questionsList.get(index);
            MorenoQuestionFace nextQuestion = questionsList.get(index + 1);
            questionsList.set(index, nextQuestion);
            questionsList.set(index + 1, currQuestion);
            return true;
        }
        return false;
    }

    @Override
    public List<MorenoQuestionFace> getQuestionsList() {
        return questionsConstView;
    }

    /**
     * Упорядочивает список респондентов по фамилии.
     */
    @Override
    public void sortRespondents(MorenoRespondentsSortOrder order) {
        Collections.sort(respondentsList, order);
    }

    @Override
    public void saveModel(File target) throws MorenoModelException {
        lastSaveDate = new Date();

        try {
            ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(target));
            OutputStreamWriter outWriter = new OutputStreamWriter(zipOut, "UTF-8"); //NOI18N

            //Сохранение метаданных
            {
                zipOut.putNextEntry(new ZipEntry(JSON_RESOURCE_METADATA));
                JSONObject jsonModel = new JSONObject();

                //model metadata
                jsonModel.put("lastId", getLastId()); //NOI18N
                jsonModel.put("creationDate", MorenoUtil.dateToString(getCreationDate())); //NOI18N
                jsonModel.put("lastSaveDate", MorenoUtil.dateToString(getLastSaveDate())); //NOI18N
                jsonModel.put("version", getVersion()); //NOI18N
                jsonModel.put("versionCompatibility", getVersionComaptibility()); //NOI18N

                //jsonModel.write(outWriter, "\n "); //NOI18N
                outWriter.write(jsonModel.toString(2));
                outWriter.flush();
                zipOut.closeEntry();
            }

            //Сохранение иссоедования
            {
                zipOut.putNextEntry(new ZipEntry(JSON_RESOURCE_RESEARCH));
                JSONObject jsonModel = new JSONObject();

                //model data
                jsonModel.put("description", getDescription()); //NOI18N
                jsonModel.put("supervisorInfo", getSupervisorInfo()); //NOI18N
                jsonModel.put("pollType", getPollType().toString()); //NOI18N
                jsonModel.put("pollDate", getPollDate()); //NOI18N
                jsonModel.put("pollLimit", getPollLimit()); //NOI18N
                jsonModel.put("conclusion", getConclusion()); //NOI18N
                jsonModel.put("researchName", getResearchName()); //NOI18N
                jsonModel.put("researchTarget", getResearchTarget()); //NOI18N
                jsonModel.put("researchGroup", getResearchGroup()); //NOI18N
                jsonModel.put("inquirerIntro", getInquirerIntro()); //NOI18N

                //jsonModel.write(outWriter, "\n "); //NOI18N
                outWriter.write(jsonModel.toString(2));
                outWriter.flush();
                zipOut.closeEntry();
            }

            //Сохранение списко респондентов
            {
                zipOut.putNextEntry(new ZipEntry(JSON_RESOURCE_RESPONDENTS));

                JSONArray jsonRespondentsList = new JSONArray();
                for (MorenoRespondentFace respondent : respondentsList) {
                    JSONObject jsonRespondent = new JSONObject();
                    jsonRespondent.put("id", respondent.getId()); //NOI18N
                    jsonRespondent.put("name", respondent.getName()); //NOI18N
                    jsonRespondent.put("sex", respondent.getSex()); //NOI18N
                    jsonRespondent.put("passedPoll", respondent.isPassedPoll()); //NOI18N
                    //jsonRespondent.put("positionX", respondent.getGraphPosition().getX()); //NOI18N
                    //jsonRespondent.put("positionY", respondent.getGraphPosition().getY()); //NOI18N
                    jsonRespondentsList.put(jsonRespondent);
                }

                //jsonRespondentsList.write(outWriter, "\n "); //NOI18N
                outWriter.write(jsonRespondentsList.toString(2));
                outWriter.flush();
                zipOut.closeEntry();
            }

            //Сохранение списка вопросов
            {
                zipOut.putNextEntry(new JarEntry(JSON_RESOURCE_QUESTIONS));

                JSONArray jsonQuestionsList = new JSONArray();
                for (MorenoQuestionFace question : questionsList) {
                    JSONObject jsonRespondent = new JSONObject();
                    jsonRespondent.put("id", question.getId()); //NOI18N
                    jsonRespondent.put("content", question.getContent()); //NOI18N
                    jsonRespondent.put("marker", question.getMarker()); //NOI18N
                    jsonRespondent.put("color", MorenoUtil.colorToString(question.getColor())); //NOI18N
                    jsonRespondent.put("type", question.getType().toString()); //NOI18N
                    jsonQuestionsList.put(jsonRespondent);
                }

                //jsonQuestionsList.write(outWriter, "\n "); //NOI18N
                outWriter.write(jsonQuestionsList.toString(2));
                outWriter.flush();
                zipOut.closeEntry();
            }

            //Сохранение списка ответов
            {
                zipOut.putNextEntry(new JarEntry(JSON_RESOURCE_ANSWERS));

                JSONArray jsonAnswersList = new JSONArray();
                for (MorenoRespondentFace resp : respondentsList) {
                    for (MorenoQuestionFace question : questionsList) {
                        MorenoAnswerFace answer = resp.getAnswerByQuestion(question);
                        for (MorenoAnswerItemFace answerItem : answer.getAnswerItems()) {
                            if (answerItem.getTarget() != null) {
                                JSONObject jsonAnswerItem = new JSONObject();
                                jsonAnswerItem.put("respondentId", answerItem.getAnswer().getRespondent().getId()); //NOI18N
                                jsonAnswerItem.put("questionId", answerItem.getAnswer().getQuestion().getId()); //NOI18N
                                jsonAnswerItem.put("positionNumber", answerItem.getQuestionItem().getItemNumber()); //NOI18N
                                jsonAnswerItem.put("targetId", answerItem.getTarget().getId()); //NOI18N
                                jsonAnswersList.put(jsonAnswerItem);
                            }
                        }
                    }
                }

                //jsonAnswersList.write(outWriter, "\n "); //NOI18N
                outWriter.write(jsonAnswersList.toString(2));
                outWriter.flush();
                zipOut.closeEntry();
            }

            //Сохранение графических данных

            //Сохранение фильтров

            //Сохранение конфигурации отображения данных
            {
                zipOut.putNextEntry(new JarEntry(JSON_RESOURCE_VIEWS));
                JSONArray outArray = new JSONArray();
                for(MorenoView2Data view:viewsList){
                    outArray.put(view.toJSON());
                }
                outWriter.write(outArray.toString(2));
                outWriter.flush();
                zipOut.closeEntry();
            }

            zipOut.close();
        } catch (JSONException ex) {
            throw new MorenoModelException(ex);
        } catch (ZipException ex) {
            throw new MorenoModelException(ex);
        } catch (IOException ex) {
            throw new MorenoModelException(ex);
        }


        //Запоминаем место последнего сохранения модели
        storeURI = target.toURI();

        //Запоминаем дату последнего сохранения модели
        lastSaveDate = new Date();
    }

    @Override
    public void loadModel(File source) throws MorenoModelException {
        //Подготовка к загрузке
        ZipInputStream zipIn = null;
        try {
            zipIn = new ZipInputStream(new FileInputStream(source));

            JSONObject jsonMetadata = null;
            JSONObject jsonResearch = null;
            JSONArray jsonRespondents = null;
            JSONArray jsonQuestions = null;
            JSONArray jsonAnswers = null;
            JSONArray jsonViews = null;

            ZipEntry zipEntry;
            while ((zipEntry = zipIn.getNextEntry()) != null) {
                if (zipEntry.getName().equals(JSON_RESOURCE_METADATA)) {
                    String json = IOUtils.toString(zipIn, "UTF-8"); //NOI18N
                    jsonMetadata = new JSONObject(json);
                } else if (zipEntry.getName().equals(JSON_RESOURCE_RESEARCH)) {
                    String json = IOUtils.toString(zipIn, "UTF-8"); //NOI18N
                    jsonResearch = new JSONObject(json);
                } else if (zipEntry.getName().equals(JSON_RESOURCE_RESPONDENTS)) {
                    String json = IOUtils.toString(zipIn, "UTF-8"); //NOI18N
                    jsonRespondents = new JSONArray(json);
                } else if (zipEntry.getName().equals(JSON_RESOURCE_QUESTIONS)) {
                    String json = IOUtils.toString(zipIn, "UTF-8"); //NOI18N
                    jsonQuestions = new JSONArray(json);
                } else if (zipEntry.getName().equals(JSON_RESOURCE_ANSWERS)) {
                    String json = IOUtils.toString(zipIn, "UTF-8"); //NOI18N
                    jsonAnswers = new JSONArray(json);
                } else if (zipEntry.getName().equals(JSON_RESOURCE_VIEWS)) {
                    String json = IOUtils.toString(zipIn, "UTF-8"); //NOI18N
                    jsonViews = new JSONArray(json);
                }
            }
            //Загрузка -  метаданных
            if (jsonMetadata == null) {
                throw new MorenoModelException(I18N.get("error.missed_metadata"));
            } else {
                creationDate = MorenoUtil.stringToDate(jsonMetadata.getString("creationDate")); //NOI18N
                lastSaveDate = MorenoUtil.stringToDate(jsonMetadata.getString("lastSaveDate")); //NOI18N
                version = jsonMetadata.getLong("version"); //NOI18N
                versionCompatibility = jsonMetadata.getLong("versionCompatibility"); //NOI18N
                lastId = jsonMetadata.getLong("lastId"); //NOI18N

                storeURI = source.toURI();
            }

            //Загрузка - данных исследования
            if (jsonResearch == null) {
                throw new MorenoModelException(I18N.get("error.missed_research_data"));
            } else {
                description = jsonGetString(jsonResearch, "description"); //NOI18N
                supervisorInfo = jsonGetString(jsonResearch, "supervisorInfo"); //NOI18N
                conclusion = jsonGetString(jsonResearch, "conclusion"); //NOI18N
                pollType = MorenoPollType.valueOf(jsonResearch.getString("pollType")); //NOI18N
                researchName = jsonGetString(jsonResearch, "researchName"); //MOI18N
                researchTarget = jsonGetString(jsonResearch, "researchTarget"); //MOI18N
                researchGroup = jsonGetString(jsonResearch, "researchGroup"); //MOI18N
                inquirerIntro = jsonGetString(jsonResearch, "inquirerIntro"); //MOI18N

                try {
                    pollLimit = jsonResearch.getInt("pollLimit"); //NOI18N
                } catch (JSONException pass) {
                    pollLimit = 0;
                }
                pollDate = jsonGetString(jsonResearch, "pollDate"); //NOI18N
            }

            //Загрузка - списка вопросов
            if (jsonQuestions == null) {
                throw new MorenoModelException(I18N.get("error.missed_question_data"));
            } else {
                for (int i = 0; i < jsonQuestions.length(); i++) {
                    JSONObject jsonQuest = (JSONObject) jsonQuestions.get(i);

                    MorenoQuestionFace quest = createQuestion();
                    quest.setId(jsonQuest.getLong("id")); //NOI18N
                    quest.setContent(notNull(jsonQuest.getString("content"))); //NOI18N
                    quest.setColor(MorenoUtil.stringToColor(jsonQuest.getString("color"))); //NOI18N
                    quest.setMarker(notNull(jsonQuest.getString("marker"))); //NOI18N
                    quest.setType(MorenoQuestionType.valueOf(jsonQuest.getString("type"))); //NOI18N
                }
            }

            //Загрузки - списка респондентов
            if (jsonRespondents == null) {
                throw new MorenoModelException(I18N.get("error.missed_respondents_data"));
            } else {
                for (int i = 0; i < jsonRespondents.length(); i++) {
                    JSONObject jsonResp = (JSONObject) jsonRespondents.get(i);

                    MorenoRespondentFace resp = createRespondent();
                    resp.setId(jsonResp.getLong("id")); //NOI18N
                    resp.setName(notNull(jsonResp.getString("name"))); //NOI18N
                    resp.setSex(MorenoSex.valueOf(jsonResp.getString("sex"))); //NOI18N
                    resp.setPassedPoll(jsonResp.getBoolean("passedPoll")); //NOI18N
                    //resp.setGraphPosition(new Point(jsonResp.getInt("positionX"), jsonResp.getInt("positionY"))); //NOI18N
                }
            }

            //Загрузка - списка ответов респондентов на вопросы
            if (jsonAnswers == null) {
                throw new MorenoModelException(I18N.get("error.missed_answers_data"));
            } else {
                Map<Long, MorenoRespondentFace> mapResp = new HashMap();
                Map<Long, MorenoQuestionFace> mapQuest = new HashMap();

                for (MorenoRespondentFace resp : respondentsList) {
                    mapResp.put(resp.getId(), resp);
                }
                for (MorenoQuestionFace quest : questionsList) {
                    mapQuest.put(quest.getId(), quest);
                }

                try {
                    for (int i = 0; i < jsonAnswers.length(); i++) {
                        JSONObject jsonAnswer = (JSONObject) jsonAnswers.get(i);

                        MorenoQuestionFace quest = mapQuest.get(jsonAnswer.getLong("questionId")); //NOI18N
                        MorenoQuestionItemFace questionItem = quest.getQuestionItemByNumber(jsonAnswer.getInt("positionNumber")); //NOI18N
                        MorenoRespondentFace resp = mapResp.get(jsonAnswer.getLong("respondentId")); //NOI18N
                        MorenoAnswerFace answer = resp.getAnswerByQuestion(quest);
                        MorenoAnswerItemFace answerItem = answer.getAnswerItemByQuestionItem(questionItem);
                        long targetId = jsonAnswer.getLong("targetId"); //NOI18N
                        MorenoRespondentFace respTarget = mapResp.get(targetId);
                        if (respTarget == null) {
                            throw new NullPointerException(I18N.get("error.no_respondent_for_id") + targetId);
                        }
                        answerItem.setTarget(respTarget);

                    }
                } catch (NullPointerException npe) {
                    throw new MorenoModelException(npe);
                }
            }

            //Загрузка конфигурации визуального отображения данных
            viewsList.clear();
            if (jsonViews != null) {
                for(int index=0;index<jsonViews.length();index++){
                    MorenoView2Data view = new MorenoView2Impl();
                    view.applyJSON(jsonViews.getJSONObject(index));
                    viewsList.add(view);
                }
            }
            if (viewsList.isEmpty()){
                viewsList.add(new MorenoView2Impl());
            }
        } catch (JSONException ex) {
            throw new MorenoModelException(ex);
        } catch (IOException ex) {
            throw new MorenoModelException(ex);
        } finally {
            if (zipIn != null) {
                try {
                    zipIn.close();
                } catch (IOException ex) {
                    Logger.getLogger(MorenoModelImpl.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }

        //инициализация после загрузки
        initViews();
        storeURI = source.toURI();
    }

    @Override
    public MorenoModelStage getStage() {
        MorenoModelStage result = MorenoModelStage.NEW_MODEL;
        return result;
    }

    private String jsonGetString(JSONObject _obj, String key){
        try {
            return _obj.getString(key);
        } catch(JSONException pass){
            return "";
        }
    }

    @Override
    public void doBatchJob(MorenoModelBatchJob batchJob) {
        if (batchJob != null) {
            batchJob.processModel(this);
        }
    }

    /* MorenoModelFire */
    @Override
    public void fireRespondentChanged(MorenoRespondentFace respondent) {
        if (fireDelegate != null) {
            fireDelegate.fireRespondentChanged(respondent);
        }
    }

    @Override
    public void fireQuestionChanged(MorenoQuestionFace question) {
        if (fireDelegate != null) {
            fireDelegate.fireQuestionChanged(question);
        }
    }

    @Override
    public void deleteQuestions(Collection<MorenoQuestionFace> questions) {
        this.questionsList.removeAll(questions);
        for(MorenoQuestionFace quest:questions){
            for(MorenoRespondentFace resp:respondentsList){
                ((MorenoRespondentImpl)resp).answersMap.remove(quest);
            }
        }
    }

    @Override
    public void deleteRespondents(Collection<MorenoRespondentFace> respondents) {
        this.respondentsList.removeAll(respondents);
    }

    protected static String notNull(String str) {
        return str != null ? str : "";
    }

    @Override
    public List<MorenoView2Data> getViewsList() {
        return viewsList;
    }

    @Override
    public String getInquirerIntro() {
        return this.inquirerIntro;
    }

    @Override
    public String getResearchGroup() {
        return this.researchGroup;
    }

    @Override
    public String getResearchName() {
        return this.researchName;
    }

    @Override
    public String getResearchTarget() {
        return this.researchTarget;
    }

    @Override
    public void setInquirerIntro(String _value) {
        this.inquirerIntro = _value;
    }

    @Override
    public void setResearchGroup(String _value) {
        this.researchGroup = _value;
    }

    @Override
    public void setResearchName(String _value) {
        this.researchName = _value;
    }

    @Override
    public void setResearchTarget(String _value) {
        this.researchTarget = _value;
    }

}
